#!/usr/bin/env python3
# -*- coding: utf-8 -*
import io
import os
import json

# The `pushd()` implementation from `https://gist.github.com/howardhamilton/537e13179489d6896dd3`
from contextlib import contextmanager

@contextmanager
def pushd(new_dir):
	previous_dir = os.getcwd()
	if len(new_dir):
		os.chdir(new_dir)
	try:
		yield
	finally:
		os.chdir(previous_dir)

def to_snake(camel_case):
	ret = ''
	last_is_upper = False
	for a in camel_case:
		if a.isupper():
			if last_is_upper == False:
				last_is_upper = True
				ret += f'_{a.lower()}'
			else:
				ret += a.lower()
		else:
			if last_is_upper == True:
				last_is_upper = False
			ret += a
	while ret[0] == '_': ret = ret[1:]
	while '__' in ret: ret = ret.replace('__', '_')
	return ret

def to_camel(snake_case, first_is_upper = False):
	ret = ''
	next_should_upper = first_is_upper
	for a in snake_case.lower():
		if a == '_':
			next_should_upper = True
		elif a.isalpha():
			if next_should_upper:
				ret += a.upper()
				next_should_upper = False
			else:
				ret += a
		else:
			ret += a
	return ret

def is_good_identifier(text):
	bad_in_identifier = ' !"#$%&\'()*+,-./:;<=>?@[\\]^`{|}~'
	if text[0].isnumeric():
		return False
	for ch in bad_in_identifier:
		if ch in text:
			return False
	return True

def parse(input, initial = {}, is_include_header = 0, handles = [], typedefs = {}, aliases = {}, structs = {}, feature_name = None):
	ret = initial
	cur_ver = ''
	sharp_if_level = 0
	is_enum = False
	is_union = False
	is_struct = False
	is_proto = False
	is_unwanted = False
	is_typedef_func = False
	is_multiline_comment = False
	macro_conditions = []
	macro_isprotos = []
	cur_func = {}
	cur_enum = {}
	cur_union = {}
	cur_struct = {}
	cur_func_name = ''
	cur_enum_name = ''
	cur_union_name = ''
	cur_struct_name = ''
	all_enum_names = set()
	all_enum_values = {}
	all_const_values = {}
	all_struct_names = set()
	must_alias = {
		'int8_t': 'i8',
		'int16_t': 'i16',
		'int32_t': 'i32',
		'int64_t': 'i64',
		'uint8_t': 'u8',
		'uint16_t': 'u16',
		'uint32_t': 'u32',
		'uint64_t': 'u64',
		'size_t': 'usize',
		'char': 'i8',
		'short': 'i16',
		'int': 'i32',
		'unsigned': 'u32',
		'long': 'i64',
		'float': 'f32',
		'double': 'f64',
		'signed char': 'i8',
		'unsigned char': 'u8',
		'signed short': 'i16',
		'unsigned short': 'u16',
		'signed int': 'i32',
		'unsigned int': 'u32',
		'signed long': 'i64',
		'unsigned long': 'u64',
		'long long': 'i64',
		'signed long long': 'i64',
		'unsigned long long': 'u64',
		'const char*': "*const i8",
	}
	must_alias |= aliases
	try:
		metadata = ret['metadata']
		all_enum_names |= set(metadata['all_enum_names'])
		all_enum_values = metadata['all_enum_values']
		all_const_values = metadata['all_const_values']
		all_struct_names |= set(metadata['all_struct_names'])
		must_alias |= metadata['must_alias']
	except KeyError:
		pass
	enabled = False
	if is_include_header:
		enabled = True
	last_line = ''
	echo_indent = '    ' * is_include_header
	cur_ver = None
	with open(input, 'r') as f:
		line_no = 0
		for line in f:
			line_no += 1
			if is_multiline_comment:
				if '*/' in line:
					line = line.split('*/', 1)[1].lstrip()
					is_multiline_comment = False
				else:
					continue
			while '/*' in line:
				if '*/' in line:
					left, right = line.split('/*', 1)
					line = left.rstrip() + ' ' + right.split('*/', 1)[1].lstrip()
				else:
					line = line.split('/*', 1)[0].rstrip()
					is_multiline_comment = True
			line = line.split('//', 1)[0].rstrip()
			trimmed_line = line.lstrip()
			indent = len(line) - len(trimmed_line)
			line = (last_line + ' ' + trimmed_line).lstrip()
			last_line = ''
			if line is None or len(line) == 0:
				continue
			if line.endswith('\\'):
				last_line += line[:-1]
				continue
			if line.startswith(('#if ', '#ifdef ', '#ifndef ')):
				sharp_if_level += 1
				macro_conditions += [is_unwanted]
				macro_isprotos += [is_proto]
				if line.startswith('#ifndef VK_NO_PROTOTYPES'):
					is_proto = True
				elif line.startswith(('#ifdef __cplusplus', '#ifdef __OBJC__')):
					is_unwanted = True
				continue
			if line.startswith("#else"):
				is_unwanted = not is_unwanted
				continue
			if line.startswith('#endif'):
				sharp_if_level -= 1
				is_proto = macro_isprotos.pop()
				is_unwanted = macro_conditions.pop()
				continue
			if line.startswith('#include'):
				print(echo_indent, end='')
				print(line)
				if '<' in line or '>' in line:
					continue
				include_file = line.split('"', 2)[1]
				include_path = os.path.dirname(include_file)
				include_file = os.path.basename(include_file)
				if include_file == 'vk_platform.h':
					print(echo_indent, end='')
					print('Skipped: "vk_platform.h"')
					continue
				with pushd(include_path):
					ret['metadata'] = {
						'all_enum_names': all_enum_names,
						'all_enum_values': all_enum_values,
						'all_const_values': all_const_values,
						'all_struct_names': all_struct_names,
						'must_alias': must_alias,
					}
					parsed = parse(include_file, ret, is_include_header + 1)
					metadata = parsed['metadata'].copy()
					del parsed['metadata']
					ret |= parsed
					all_enum_names |= set(metadata['all_enum_names'])
					all_enum_values |= metadata['all_enum_values']
					all_const_values |= metadata['all_const_values']
					all_struct_names |= set(metadata['all_struct_names'])
					must_alias |= metadata['must_alias']
				continue
			if enabled == False:
				if line.startswith('#define VK_VERSION_1_0 1'):
					enabled = True
				else:
					continue
			if line.startswith(('#define VK_', '#define vulkan_')) and line.endswith(' 1') and indent == 0 and '_SPEC_VERSION ' not in line:
				is_first_ver = False
				if cur_ver is None:
					is_first_ver = True
				cur_ver = line.split(' ', 2)[1]
				ret[cur_ver] = {
					'typedefs': {},
					'handles': [],
					'non_dispatchable_handles': [],
					'constants': {},
					'typed_constants': {},
					'enums': {},
					'unions': {},
					'structs': {},
					'funcs': [],
					'func_protos': {},
				}
				if is_first_ver:
					ret[cur_ver]['handles'] = list(set(ret[cur_ver]['handles']) | set(handles))
					ret[cur_ver]['typedefs'] |= typedefs
					ret[cur_ver]['structs'] |= structs
				if feature_name is not None:
					ret[cur_ver]['feature'] = feature_name
				continue
			if is_unwanted:
				print(echo_indent, end='')
				print(f'Skip filtered code: {line}')
				continue
			if line.startswith('#define ') and line.endswith('_H_ 1'):
				print(echo_indent, end='')
				print(f'Header: {line}')
				continue
			if cur_ver == '':
				print(echo_indent, end='')
				print(f'Unversioned line: {line}')
				continue
			if line.startswith('#define '):
				parts = line.split(' ', 2)
				ident, value = parts[1], parts[2].strip()
				if '(' in ident or ')' in ident or ident == 'VK_USE_64_BIT_PTR_DEFINES':
					continue
				while f'{value[0]}{value[-1]}' == '()':
					value = value[1:-1]
				def try_redir(ident):
					ident = ident.strip()
					try:
						val = all_const_values[ident]
					except KeyError:
						val = ident
					return int(val)
				def vk_make_version(major_minor_patch):
					major, minor, patch = major_minor_patch.split('(', 1)[1].split(')', 1)[0].split(',')
					major, minor, patch = try_redir(major), try_redir(minor), try_redir(patch)
					return hex((major << 22) | (minor << 12) | patch)
				def vk_make_api_version(variant_major_minor_patch):
					variant, major, minor, patch = variant_major_minor_patch.split('(', 1)[1].split(')', 1)[0].split(',')
					variant, major, minor, patch = try_redir(variant), try_redir(major), try_redir(minor), try_redir(patch)
					return hex((variant << 29) | (major << 22) | (minor << 12) | patch)
				def vk_make_video_std_version(major_minor_patch):
					major, minor, patch = major_minor_patch.split('(', 1)[1].split(')', 1)[0].split(',')
					major, minor, patch = try_redir(major), try_redir(minor), try_redir(patch)
					return hex((major << 22) | (minor << 12) | patch)
				if value.startswith('VK_MAKE_VERSION'):
					value = vk_make_version(value[len('VK_MAKE_VERSION'):])
				elif value.startswith('VK_MAKE_API_VERSION'):
					value = vk_make_api_version(value[len('VK_MAKE_API_VERSION'):])
				elif value.startswith('VK_MAKE_VIDEO_STD_VERSION'):
					value = vk_make_video_std_version(value[len('VK_MAKE_VIDEO_STD_VERSION'):])
				if ident != cur_ver:
					ret[cur_ver]['constants'][ident] = value
					all_const_values |= {ident: value}
				continue
			if is_enum:
				if line.startswith('}'):
					is_enum = False
					ret[cur_ver]['enums'][cur_enum_name] = cur_enum
					cur_enum = {}
					continue
				if '=' in line:
					if line.endswith(','):
						line = line[:-1]
					name, value = line.split('=', 1)
					cur_enum |= {name.strip(): value.strip()}
					all_enum_values |= {name.strip(): [value.strip(), cur_enum_name]}
				else:
					print(echo_indent, end='')
					print(f'Unknown data in enum at line {line_no}: {line}')
				continue
			elif is_union:
				if line.startswith('}'):
					is_union = False
					ret[cur_ver]['unions'][cur_union_name] = cur_union
					cur_union = {}
					continue
				if line.endswith(';'):
					line = line[:-1]
					type, name = line.rsplit(' ', 1)
					cur_union |= {name.strip(): type.strip()}
				else:
					print(echo_indent, end='')
					print(f'Unknown data in union at line {line_no}: {line}')
				continue
			elif is_struct:
				while ' :' in line or ': ' in line:
					line = line.replace(' :', ':').replace(': ', ':')
				if line.startswith('}'):
					is_struct = False
					ret[cur_ver]['structs'][cur_struct_name] = cur_struct
					cur_struct = {}
					continue
				if line.endswith(';'):
					line = line[:-1]
					type, name = line.rsplit(' ', 1)
					cur_struct |= {name.strip(): type.strip()}
				else:
					print(echo_indent, end='')
					print(f'Unknown data in struct at line {line_no}: {line}')
				continue
			elif is_proto:
				if line.startswith('VKAPI_ATTR '):
					if line.endswith('('):
						ret[cur_ver]['funcs'] += [line[:-1].rsplit(' ', 1)[-1]]
					else:
						print(echo_indent, end='')
						print(f'Unknown data in function declaration at line {line_no}: {line}')
				continue
			elif is_typedef_func:
				if line.endswith(');'):
					is_typedef_func = False
					line = line[:-2]
				elif line.endswith(','):
					line = line[:-1]
				for param in line.split(','):
					param_type, param_name = param.rsplit(' ', 1)
					cur_func['params'] |= {param_name.strip(): param_type.strip()}
				if is_typedef_func == False:
					ret[cur_ver]['func_protos'][cur_func_name] = cur_func.copy()
					cur_func = {}
				continue
			else:
				if line.startswith('typedef enum '):
					is_enum = True
					cur_enum_name = line[len('typedef enum '):].split(' ', 1)[0]
					cur_enum = {}
					all_enum_names |= {cur_enum_name}
					continue
				if line.startswith('typedef union '):
					is_union = True
					cur_union_name = line[len('typedef union '):].split(' ', 1)[0]
					cur_union = {}
					continue
				if line.startswith('typedef struct '):
					if line[-1] == ';' and '{' not in line and '*' in line:
						handle_name = line.rsplit(' ', 1)[-1][:-1].strip()
						ret[cur_ver]['handles'] += [handle_name]
						print(echo_indent, end='')
						print(f'Parsed `{line}` as handle definition: {handle_name}')
					else:
						is_struct = True
						cur_struct = {}
						cur_struct_name = line[len('typedef struct '):].split(' ', 1)[0]
						all_struct_names |= {cur_struct_name}
					continue
				if line.startswith('typedef '):
					if 'VKAPI_PTR' in line:
						cur_func_name = line.split('VKAPI_PTR *', 1)[1].split(')', 1)[0]
						if line.endswith('('):
							is_typedef_func = True
							params = {}
						elif line.endswith(');'):
							params = {}
							for param in line.split(f'{cur_func_name})(', 1)[1].rsplit(')', 1)[0].split(','):
								if param == 'void':
									pass
								elif ' ' in param:
									type, name = param.rsplit(' ', 1)
									params |= {name.strip(): type.strip()}
								else:
									params |= {f'_param_{params.len()}': type.strip()}
						else:
							print(echo_indent, end='')
							print(f'Unknown data in function prototype at line {line_no}: {line}')
							continue
						cur_func = {
							'ret_type': line[len('typedef '):].split('(', 1)[0].strip(),
							'params': params,
						}
						if is_typedef_func == False:
							ret[cur_ver]['func_protos'][cur_func_name] = cur_func.copy()
							cur_func = {}
					else:
						if line.endswith(';'):
							line = line[:-1]
							type, name = line[len('typedef '):].rsplit(' ', 1)
							ret[cur_ver]['typedefs'] |= {name: type}
						else:
							print(echo_indent, end='')
							print(f'Unknown data in typedef at line {line_no}: {line}')
					continue
				if line.startswith('struct ') and line[-1] == ';':
					identifier = line[len('struct '):-1]
					if is_good_identifier(identifier):
						print(echo_indent, end='')
						print(f'Parsed `{line}` as handle definition: {identifier}')
						ret[cur_ver]['handles'] += [identifier]
					else:
						print(echo_indent, end='')
						print(f'Unknown struct at line {line_no}: {line}')
					continue
				if line.startswith('static const ') and line[-1] == ';':
					type_ident, value = line[len('static const '):-1].split('=')
					type_, ident = type_ident.strip().split(' ', 1)
					value = value.strip()
					ret[cur_ver]['typed_constants'][ident] = [value, type_]
					all_const_values |= {ident: value}
					continue
				if line.startswith('VK_DEFINE_HANDLE'):
					handle_name = line.split('(', 1)[1].split(')', 1)[0]
					ret[cur_ver]['handles'] += [handle_name]
					continue
				if line.startswith('VK_DEFINE_NON_DISPATCHABLE_HANDLE'):
					handle_name = line.split('(', 1)[1].split(')', 1)[0]
					ret[cur_ver]['non_dispatchable_handles'] += [handle_name]
					continue
				print(echo_indent, end='')
				print(f'Unknown line {line_no}: {line}')
	ret['metadata'] = {
		'all_enum_names': list(all_enum_names),
		'all_enum_values': all_enum_values,
		'all_const_values': all_const_values,
		'all_struct_names': list(all_struct_names),
		'must_alias': must_alias,
	}
	return ret

def to_rust(outfile, parsed):
	metadata = parsed['metadata']
	all_enum_names = set(metadata['all_enum_names'])
	all_enum_values = metadata['all_enum_values']
	all_const_values = metadata['all_const_values']
	all_struct_names = set(metadata['all_struct_names'])
	must_alias = metadata['must_alias']
	def ctype_to_rust(ctype):
		ctype = ctype.replace(' *', '*')
		try:
			rust = must_alias[ctype]
			ctype = ''
		except KeyError:
			rust = ''
		while len(ctype):
			if ctype.endswith('const*'):
				ctype = ctype[:-len('const*')].rstrip()
				rust = f'*const {rust}'
			elif ctype.endswith('*'):
				if ctype.startswith('const '):
					ctype = ctype[len('const '):-1]
					rust = f'*const {rust}'
				else:
					ctype = ctype[:-1]
					rust = f'*mut {rust}'
			else:
				if ctype.startswith('struct '):
					ctype = ctype[len('struct '):].strip()
				if ctype == 'void': ctype = 'c_void'
				rust = f'{rust.strip()} {ctype}'.strip()
				break
		return rust
	def process_guts(name, type, is_param = False):
		is_array = False
		type = ctype_to_rust(type)
		if type.startswith('const '):
			type = type[len('const '):]
		while '[' in name:
			is_array = True
			name, size = name.rsplit('[', 1)
			size = size.split(']', 1)[0]
			if size.isnumeric():
				type = f'[{type}; {size}_usize]'
			else:
				type = f'[{type}; {size} as usize]'
		if is_param and is_array: type = f'&{type}'
		if name == 'type': name = f'{name}_'
		return name, type
	def process_constant_value(value):
		while True:
			try:
				value = all_const_values[value]
			except KeyError:
				break
		while True:
			try:
				value, source = all_enum_values[value]
			except KeyError:
				break
		type_ = None
		value = value.lower()
		if f'{value[0]}{value[-1]}' == '""' and len(value) >= 2:
			type_ = '&str'
		elif value.endswith('ull'):
			type_ = 'u64'
			value = f'{value[:-3]}u64'
		elif value.endswith('ll'):
			type_ = 'i64'
			value = f'{value[:-2]}i64'
		elif value.endswith('u'):
			type_ = 'u32'
			value = f'{value[:-1]}u32'
		elif value.endswith('l'):
			type_ = 'i32'
			value = f'{value[:-1]}i32'
		elif value.endswith('f') and '.' in value:
			type_ = 'f32'
			value = f'{value[:-1]}f32'
		if value[0] == '~':
			value = f'!{value[1:]}'
		if type_ is None:
			type_ = 'u32'
		return value, type_
	def union_member_type_process(union, type):
		is_enum = type in all_enum_names
		if is_enum:
			struct.write(f'\t/// Original type: {type}\n')
			type = 'u32'
		try:
			type = must_alias[type]
		except KeyError:
			pass
		return type
	def struct_member_type_process(struct, type):
		is_pointer = type.startswith(('&', '*'))
		is_enum = type in all_enum_names
		if is_pointer:
			struct.write(f'\t/// Original type: {type}\n')
			type = 'usize'
		elif is_enum:
			struct.write(f'\t/// Original type: {type}\n')
			type = 'u32'
		try:
			type = must_alias[type]
		except KeyError:
			pass
		return type
	vk_struct = io.StringIO()
	vk_traits = io.StringIO()
	vk_s_impl = io.StringIO()
	vk_g_impl = io.StringIO()
	vk_struct.write('/// The all-in-one struct for your Vulkan APIs\n')
	vk_struct.write('#[derive(Default, Clone, Debug)]\n')
	vk_struct.write('pub struct VkCore {\n')
	vk_struct.write('\t/// The Vulkan instance\n')
	vk_struct.write(f'\tinstance: Arc<VkInstanceWrap>,\n')
	vk_struct.write('\t/// The Vulkan extension strings\n')
	vk_struct.write(f'\textensions: BTreeSet<String>,\n')
	vk_struct.write('\t/// The application info\n')
	vk_struct.write(f'\tapp_info: VkApplicationInfo,\n')
	vk_s_impl.write('\n')
	vk_s_impl.write('impl Default for VkApplicationInfo {\n')
	vk_s_impl.write('\tfn default() -> Self {\n')
	vk_s_impl.write('\t\tlet app_name = CString::new("<APPLICATION NAME>").unwrap();\n')
	vk_s_impl.write('\t\tlet engine_name = CString::new("<ENGINE_NAME>").unwrap();\n')
	vk_s_impl.write('\t\tSelf {\n')
	vk_s_impl.write('\t\t\tsType: VkStructureType::VK_STRUCTURE_TYPE_APPLICATION_INFO,\n')
	vk_s_impl.write('\t\t\tpNext: null(),\n')
	vk_s_impl.write('\t\t\tpApplicationName: app_name.as_ptr(),\n')
	vk_s_impl.write('\t\t\tapplicationVersion: vk_make_version(1, 0, 0),\n')
	vk_s_impl.write('\t\t\tpEngineName: engine_name.as_ptr(),\n')
	vk_s_impl.write('\t\t\tengineVersion: vk_make_version(1, 0, 0),\n')
	vk_s_impl.write('\t\t\tapiVersion: VK_API_VERSION_1_4,\n')
	vk_s_impl.write('\t\t}\n')
	vk_s_impl.write('\t}\n')
	vk_s_impl.write('}\n')
	vk_s_impl.write('\n')
	vk_s_impl.write('#[derive(Default, Debug, Clone, Copy)]\n')
	vk_s_impl.write('pub struct VkInstanceWrap(VkInstance);\n')
	vk_s_impl.write('\n')
	vk_s_impl.write('unsafe impl Send for VkInstanceWrap {}\n')
	vk_s_impl.write('unsafe impl Sync for VkInstanceWrap {}\n')
	vk_s_impl.write('\n')
	vk_s_impl.write('impl Deref for VkInstanceWrap {\n')
	vk_s_impl.write('\ttype Target = VkInstance;\n')
	vk_s_impl.write('\tfn deref(&self) -> &Self::Target {\n')
	vk_s_impl.write('\t\t&self.0\n')
	vk_s_impl.write('\t}\n')
	vk_s_impl.write('}\n')
	vk_s_impl.write('\n')
	vk_s_impl.write('impl VkCore {\n')
	vk_s_impl.write('\t/// Create the all-in-one struct for your Vulkan APIs by a `get_instance_proc_address()` function\n')
	vk_s_impl.write('\t/// You have to provide the `VkApplicationInfo` struct to specify your application info\n')
	vk_s_impl.write('\t/// You can use `vk_make_version()`/`vk_make_api_version()` from this crate\n')
	vk_s_impl.write("\tpub fn new(app_info: VkApplicationInfo, mut get_instance_proc_address: impl FnMut(VkInstance, &'static str) -> *const c_void) -> Result<Self> {\n")
	vk_s_impl.write('\t\tlet vkEnumerateInstanceExtensionProperties = get_instance_proc_address(null(), "vkEnumerateInstanceExtensionProperties");\n')
	vk_s_impl.write('\t\tif vkEnumerateInstanceExtensionProperties.is_null() {\n')
	vk_s_impl.write('\t\t\treturn Err(VkError::NullFunctionPointer("vkEnumerateInstanceExtensionProperties"));\n')
	vk_s_impl.write('\t\t}\n')
	vk_s_impl.write('\t\tlet vkEnumerateInstanceExtensionProperties: PFN_vkEnumerateInstanceExtensionProperties = unsafe{transmute(vkEnumerateInstanceExtensionProperties)};\n')
	vk_s_impl.write('\t\tlet mut ext_count: u32 = 0;\n')
	vk_s_impl.write('\t\tvk_result_conv("vkEnumerateInstanceExtensionProperties", vkEnumerateInstanceExtensionProperties(null(), &mut ext_count, null_mut()))?;\n')
	vk_s_impl.write('\t\tlet mut extensions: Vec<VkExtensionProperties> = Vec::with_capacity(ext_count as usize);\n')
	vk_s_impl.write('\t\tvk_result_conv("vkEnumerateInstanceExtensionProperties", vkEnumerateInstanceExtensionProperties(null(), &mut ext_count, extensions.as_mut_ptr()))?;\n')
	vk_s_impl.write('\t\tunsafe {extensions.set_len(ext_count as usize)};\n')
	vk_s_impl.write('\t\tlet ext_pointers: Vec<*const i8> = extensions.iter().map(|prop|prop.extensionName.as_ptr()).collect();\n')
	vk_s_impl.write('\t\tlet extension_strings: Vec<String> = ext_pointers.iter().map(|p|unsafe {CStr::from_ptr(*p)}.to_string_lossy().to_string()).collect();\n')
	vk_s_impl.write('\t\t#[allow(unused_mut)]\n')
	vk_s_impl.write("\t\tlet mut layers: Vec<&'static str> = Vec::new();\n")
	vk_s_impl.write('\t\t#[cfg(feature = "validation_layer")]\n')
	vk_s_impl.write('\t\tlayers.push("VK_LAYER_KHRONOS_validation\\0");\n')
	vk_s_impl.write('\t\t#[cfg(feature = "api_dump")]\n')
	vk_s_impl.write('\t\tlayers.push("VK_LAYER_LUNARG_api_dump\\0");\n')
	vk_s_impl.write('\t\tlet layer_ptrs: Vec<*const i8> = layers.iter().map(|l|l.as_ptr() as *const i8).collect();\n')
	vk_s_impl.write('\t\tlet create_info = VkInstanceCreateInfo {\n')
	vk_s_impl.write('\t\t\tsType: VkStructureType::VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,\n')
	vk_s_impl.write('\t\t\tpNext: null(),\n')
	vk_s_impl.write('\t\t\tflags: 0,\n')
	vk_s_impl.write('\t\t\tenabledLayerCount: layer_ptrs.len() as u32,\n')
	vk_s_impl.write('\t\t\tpApplicationInfo: &app_info,\n')
	vk_s_impl.write('\t\t\tppEnabledLayerNames: layer_ptrs.as_ptr(),\n')
	vk_s_impl.write('\t\t\tenabledExtensionCount: ext_pointers.len() as u32,\n')
	vk_s_impl.write('\t\t\tppEnabledExtensionNames: ext_pointers.as_ptr()\n')
	vk_s_impl.write('\t\t};\n')
	vk_s_impl.write('\t\tlet vkCreateInstance = get_instance_proc_address(null(), "vkCreateInstance");\n')
	vk_s_impl.write('\t\tif vkCreateInstance.is_null() {\n')
	vk_s_impl.write('\t\t\treturn Err(VkError::NullFunctionPointer("vkCreateInstance"))\n')
	vk_s_impl.write('\t\t}\n')
	vk_s_impl.write('\t\tlet vkCreateInstance: PFN_vkCreateInstance = unsafe{transmute(vkCreateInstance)};\n')
	vk_s_impl.write('\t\tlet mut instance: VkInstance = null();\n')
	vk_s_impl.write('\t\tvk_result_conv("vkCreateInstance", vkCreateInstance(&create_info, null(), &mut instance))?;\n')
	vk_s_impl.write('\t\t\n')
	vk_s_impl.write('\t\tOk(Self {\n')
	vk_s_impl.write('\t\t\tinstance: Arc::new(VkInstanceWrap(instance)),\n')
	vk_s_impl.write('\t\t\textensions: extension_strings.into_iter().collect(),\n')
	vk_s_impl.write('\t\t\tapp_info,\n')
	def process_version(version, verdata, f):
		nonlocal vk_struct, vk_traits, vk_s_impl, vk_g_impl
		constants = verdata['constants']
		typed_constants = verdata['typed_constants']
		typedefs = verdata['typedefs']
		handles = verdata['handles']
		non_dispatchable_handles = verdata['non_dispatchable_handles']
		enums = verdata['enums']
		unions = verdata['unions']
		structs = verdata['structs']
		func_protos = verdata['func_protos']
		funcs = verdata['funcs']
		try:
			feature = verdata['feature']
			feature_inline = f'#[cfg(feature = "{feature}")] '
			feature = f'#[cfg(feature = "{feature}")]\n'
			feature_indent = f'\t{feature}'
			feature_indent_3 = f'\t\t\t{feature}'
		except KeyError:
			feature_inline = ''
			feature = ''
			feature_indent = ''
			feature_indent_3 = ''
		def is_bitfield_enum(typename):
			nonlocal enums
			suffix = ''
			while typename[-1].isupper():
				suffix = typename[-1] + suffix
				typename = typename[:-1]
				if len(typename) == 0:
					return None, None
			if typename[-1] == 's':
				typename = typename[:-1]
			enumbf_type = f'{typename}Bits{suffix}'
			try:
				enumbf_data = enums[enumbf_type]
			except KeyError:
				enumbf_type = None
				enumbf_data = None
			return enumbf_type, enumbf_data
		def is_the_enum_bitfield(enumname):
			try:
				typename, suffix = enumname.rsplit("Bits", 1)
			except ValueError:
				return None, None
			typename += 's'
			if typename in typedefs:
				return typename, suffix
			else:
				return None, None
		def process_constants(f):
			for constant, value in constants.items():
				constval, consttype = process_constant_value(value)
				f.write(f'/// constant `{constant}` from {version}\n')
				if not constant.startswith('StdVideo'):
					f.write(f'/// - Reference: <https://registry.khronos.org/vulkan/specs/latest/man/html/{constant}.html>\n')
				f.write(feature)
				f.write(f'pub const {constant}: {consttype} = {constval};\n')
			for constant, (value) in typed_constants.items():
				constval, consttype = value
				constval, infertype = process_constant_value(constval)
				f.write(f'/// constant `{constant}` from {version}\n')
				if not constant.startswith('StdVideo'):
					f.write(f'/// - Reference: <https://registry.khronos.org/vulkan/specs/latest/man/html/{constant}.html>\n')
				f.write(feature)
				f.write(f'pub const {constant}: {consttype} = {constval};\n')
		def process_typedefs(f):
			for type, tname in typedefs.items():
				tname = ctype_to_rust(tname)
				if type not in must_alias:
					f.write(f'/// type definition `{type}` from {version}\n')
					if not type.startswith('StdVideo'):
						f.write(f'/// - Reference: <https://registry.khronos.org/vulkan/specs/latest/man/html/{type}.html>\n')
				else:
					f.write(f'/// type definition for Rust: `{type}` = `{tname}`\n')
					f.write(f'/// - Reference: <https://en.cppreference.com/w/cpp/types/integer.html>\n')
				if is_good_identifier(type):
					f.write(feature)
					f.write('#[allow(non_camel_case_types)]\n')
					f.write(f'pub type {type} = {tname};\n')
				else:
					while type.endswith('*'):
						type = type[:-1].rstrip()
						if type.endswith('const'):
							type = type[:-len('const')]
					type = type.rsplit(' ', 1)[-1]
					if is_good_identifier(type):
						f.write(feature)
						f.write('#[allow(non_camel_case_types)]\n')
						f.write(f'pub type {type} = {tname};\n')
				enumbf_type, enumbf_data = is_bitfield_enum(type)
				if enumbf_type is not None:
					f.write(f'/// Convert `{type}` to `String`, showing the composition of the bits from the member of `{enumbf_type}`\n')
					f.write(feature)
					f.write(f'pub fn {to_snake(type)}_to_string(value: {type}) -> String {{\n')
					f.write(f'\tlet mut flags = Vec::<&str>::with_capacity({len(enumbf_data)});\n')
					for enum_string in enumbf_data:
						f.write(f'\tif (value & {enumbf_type}::{enum_string} as {type}) == {enumbf_type}::{enum_string} as {type} {{\n')
						f.write(f'\t\tflags.push("{enumbf_type}::{enum_string}");\n')
						f.write('\t}\n')
					f.write(f'\tlet ret = flags.join(" | ");\n')
					f.write('\tif !ret.is_empty() {\n')
					f.write('\t\tret\n')
					f.write('\t} else {\n')
					f.write('\t\tString::from("0")\n')
					f.write('\t}\n')
					f.write('}\n')
		def process_handles(f):
			for handle in handles:
				f.write(f'/// Normal handle `{handle}` from {version}\n')
				if not handle.startswith('StdVideo'):
					f.write(f'/// - Reference: <https://registry.khronos.org/vulkan/specs/latest/man/html/{handle}.html>\n')
				f.write(f'{feature_inline}#[repr(C)] #[derive(Debug, Clone, Copy)] pub struct {handle}_T {{_unused: u32,}}\n')
				f.write(f'{feature_inline}pub type {handle} = *const {handle}_T;\n')
			for handle in non_dispatchable_handles:
				f.write(f'/// Non-dispatchable handle `{handle}` from {version}\n')
				if not handle.startswith('StdVideo'):
					f.write(f'/// - Reference: <https://registry.khronos.org/vulkan/specs/latest/man/html/{handle}.html\n')
				f.write(f'{feature_inline}#[cfg(target_pointer_width = "32")] pub type {handle} = u64;\n')
				f.write(f'{feature_inline}#[cfg(target_pointer_width = "64")] #[repr(C)] #[derive(Debug, Clone, Copy)] pub struct {handle}_T {{_unused: u32,}}\n')
				f.write(f'{feature_inline}#[cfg(target_pointer_width = "64")] pub type {handle} = *const {handle}_T;\n')
		def process_enums(f):
			for enum, enumpair in enums.items():
				already_values = {}
				asso = io.StringIO()
				f.write(f'/// enum `{enum}` from {version}\n')
				if not enum.startswith('StdVideo'):
					f.write(f'/// - Reference: <https://registry.khronos.org/vulkan/specs/latest/man/html/{enum}.html>\n')
				f.write(feature)
				f.write('#[repr(C)]\n')
				f.write('#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]\n')
				f.write('#[allow(non_camel_case_types)]\n')
				f.write(f'pub enum {enum} {{\n')
				for enumname, enumval in enumpair.items():
					try:
						enumdef, enumfrom = all_enum_values[enumval]
						asso.write(f'\tpub const {enumname}: {enumfrom} = {enumfrom}::{enumval};\n')
					except KeyError:
						enumval, valtype = process_constant_value(enumval)
						try:
							enumalias = already_values[enumval]
							asso.write(f'\tpub const {enumname}: {enum} = {enum}::{enumalias};\n')
						except KeyError:
							f.write(f'\t{enumname} = {enumval},\n')
							already_values |= {enumval: enumname}
				f.write('}\n')
				typename, suffix = is_the_enum_bitfield(enum)
				if typename is not None:
					f.write(f'impl BitAnd for {enum} {{\n')
					f.write(f'\ttype Output = {typename};\n')
					f.write(f'\tfn bitand(self, rhs: Self) -> {typename} {{\n')
					f.write(f'\t\tself as {typename} & rhs as {typename}\n')
					f.write('\t}\n')
					f.write('}\n')
					f.write(f'impl BitAnd<{typename}> for {enum} {{\n')
					f.write(f'\ttype Output = {typename};\n')
					f.write(f'\tfn bitand(self, rhs: {typename}) -> {typename} {{\n')
					f.write(f'\t\tself as {typename} & rhs\n')
					f.write('\t}\n')
					f.write('}\n')
					f.write(f'impl BitOr for {enum} {{\n')
					f.write(f'\ttype Output = {typename};\n')
					f.write(f'\tfn bitor(self, rhs: Self) -> {typename} {{\n')
					f.write(f'\t\tself as {typename} | rhs as {typename}\n')
					f.write('\t}\n')
					f.write('}\n')
					f.write(f'impl BitOr<{typename}> for {enum} {{\n')
					f.write(f'\ttype Output = {typename};\n')
					f.write(f'\tfn bitor(self, rhs: {typename}) -> {typename} {{\n')
					f.write(f'\t\tself as {typename} | rhs\n')
					f.write('\t}\n')
					f.write('}\n')
					f.write(f'impl BitXor for {enum} {{\n')
					f.write(f'\ttype Output = {typename};\n')
					f.write(f'\tfn bitxor(self, rhs: Self) -> {typename} {{\n')
					f.write(f'\t\tself as {typename} ^ rhs as {typename}\n')
					f.write('\t}\n')
					f.write('}\n')
					f.write(f'impl BitXor<{typename}> for {enum} {{\n')
					f.write(f'\ttype Output = {typename};\n')
					f.write(f'\tfn bitxor(self, rhs: {typename}) -> {typename} {{\n')
					f.write(f'\t\tself as {typename} ^ rhs\n')
					f.write('\t}\n')
					f.write('}\n')
					f.write(f'impl Not for {enum} {{\n')
					f.write(f'\ttype Output = {typename};\n')
					f.write(f'\tfn not(self) -> {typename} {{\n')
					f.write(f'\t\t!(self as {typename})\n')
					f.write('\t}\n')
					f.write('}\n')
					asso.write(f'\t/// Combine bits together\n')
					asso.write(f'\tpub fn combine(bits: &[Self]) -> {typename} {{\n')
					asso.write('\t\tlet mut ret = 0;\n')
					asso.write('\t\tfor bit in bits {\n')
					asso.write(f'\t\t\tret |= *bit as {typename};\n')
					asso.write('\t\t}\n')
					asso.write('\t\tret\n')
					asso.write('\t}\n')
					asso.write(f'\t/// Check if the bitfield has a value\n')
					asso.write(f'\tpub fn check(value: {typename}, bits: Self) -> bool {{\n')
					asso.write('\t\t(bits & value) == value\n')
					asso.write('\t}\n')
				asso = asso.getvalue()
				if len(asso):
					f.write(f'impl {enum} {{\n')
					f.write(asso)
					f.write('}\n')
		def process_unions(f):
			for union_name, union_guts in unions.items():
				f.write(f'/// union `{union_name}` from {version}\n')
				if not union_name.startswith('StdVideo'):
					f.write(f'/// - Reference: <https://registry.khronos.org/vulkan/specs/latest/man/html/{union_name}.html>\n')
				f.write(feature)
				f.write('#[repr(C)]\n')
				f.write('#[derive(Clone, Copy)]\n')
				f.write(f'pub union {union_name} {{\n')
				for name, type in union_guts.items():
					name, type = process_guts(name, type)
					f.write(f'\tpub {name}: {type},\n')
				f.write('}\n')
				f.write(feature)
				f.write(f'impl Debug for {union_name} {{\n')
				f.write('\tfn fmt(&self, f: &mut Formatter) -> fmt::Result {\n')
				f.write(f'\t\tf.debug_struct("{union_name}")\n')
				for name, type in union_guts.items():
					name = name.split('[', 1)[0]
					f.write(f'\t\t.field("{name}", unsafe {{&self.{name}}})\n')
				f.write('\t\t.finish()\n')
				f.write('\t}\n')
				f.write('}\n')
		def process_structs(f):
			for struct_name, struct_guts in structs.items():
				f.write(f'/// struct `{struct_name}` from {version}\n')
				if not struct_name.startswith('StdVideo'):
					f.write(f'/// - Reference: <https://registry.khronos.org/vulkan/specs/latest/man/html/{struct_name}.html>\n')
				has_bitfield = False
				num_bitfields = 0
				last_bits = 0
				struct = io.StringIO()
				s_impl = io.StringIO()
				d_impl = io.StringIO()
				f.write(feature)
				struct.write('#[repr(C)]\n')
				if bool(struct_guts):
					struct.write('#[derive(Clone, Copy)]\n')
				else:
					struct.write('#[derive(Debug, Clone, Copy)]\n')
				struct.write(f'pub struct {struct_name} {{\n')
				if bool(struct_guts):
					d_impl.write(feature)
					d_impl.write(f'impl Debug for {struct_name} {{\n')
					d_impl.write('\tfn fmt(&self, f: &mut Formatter) -> fmt::Result {\n')
					d_impl.write(f'\t\tf.debug_struct("{struct_name}")\n')
				have_special_fields = False
				for name, type in struct_guts.items():
					name, type = process_guts(name, type)
					enumbf_type, enumbf_data = is_bitfield_enum(type)
					if ':' in name:
						if has_bitfield == False:
							has_bitfield = True
							num_bitfields = 1
							s_impl.write(feature)
							s_impl.write(f'impl {struct_name} {{\n')
						name, bits = name.split(':', 1)
						bits = int(bits)
						bf_name = f'bitfield{num_bitfields}'
						struct.write(f'\t/// Bitfield: {name}: {type} in {bits} bits\n')
						s_impl.write(f'\tpub fn get_{name}(&self) -> u32 {{\n')
						if last_bits:
							s_impl.write(f'\t\t(self.{bf_name} >> {last_bits}) & {hex((1 << bits) - 1)}\n')
						else:
							s_impl.write(f'\t\tself.{bf_name} & {hex((1 << bits) - 1)}\n')
						s_impl.write('\t}\n')
						s_impl.write(f'\tpub fn set_{name}(&mut self, value: u32) {{\n')
						if last_bits:
							s_impl.write(f'\t\tself.{bf_name} = (value & {hex((1 << bits) - 1)}) << {last_bits};\n')
						else:
							s_impl.write(f'\t\tself.{bf_name} = value & {hex((1 << bits) - 1)};\n')
						s_impl.write('\t}\n')
						if enumbf_type is not None:
							d_impl.write(f'\t\t.field("{name}", &format_args!("{{}}", {to_snake(type)}_to_string(self.get_{name}())))\n')
							have_special_fields = True
						else:
							d_impl.write(f'\t\t.field("{name}", &self.get_{name}())\n')
						last_bits += bits
						last_bits %= 32
						if last_bits == 0:
							struct.write(f'\t{bf_name}: u32,\n')
							num_bitfields += 1
					else:
						if last_bits:
							bf_name = f'bitfield{num_bitfields}'
							struct.write(f'\tpub {bf_name}: u32,\n')
							num_bitfields += 1
							last_bits = 0
						struct.write(f'\tpub {name}: {type},\n')
						if enumbf_type is not None:
							d_impl.write(f'\t\t.field("{name}", &format_args!("{{}}", {to_snake(type)}_to_string(self.{name})))\n')
							have_special_fields = True
						elif type.startswith('[i8; '):
							d_impl.write(f'\t\t.field("{name}", &format_args!("{{}}", vk_format_maybe_string(&self.{name})))\n')
							have_special_fields = True
						elif type.startswith('[u8; '):
							d_impl.write(f'\t\t.field("{name}", &format_args!("{{}}", vk_to_byte_array_string(&self.{name})))\n')
							have_special_fields = True
						else:
							d_impl.write(f'\t\t.field("{name}", &self.{name})\n')
				if last_bits:
					bf_name = f'bitfield{num_bitfields}'
					struct.write(f'\tpub {bf_name}: u32,\n')
				struct.write('}\n')
				if has_bitfield:
					s_impl.write('}\n')
				if bool(struct_guts):
					d_impl.write('\t\t.finish()\n')
					d_impl.write('\t}\n')
					d_impl.write('}\n')
				if have_special_fields:
					f.write(struct.getvalue())
					f.write(s_impl.getvalue())
					f.write(d_impl.getvalue())
				else:
					f.write(struct.getvalue().replace('#[derive(Clone, Copy)]', '#[derive(Debug, Clone, Copy)]'))
					f.write(s_impl.getvalue())
		def proc_protos(f):
			for functype_name, func_data in func_protos.items():
				funcname = functype_name.split('PFN_', 1)[-1]
				f.write(f'/// function prototype `{functype_name}` from {version}\n')
				f.write(f'/// - Reference: <https://registry.khronos.org/vulkan/specs/latest/man/html/{funcname}.html>\n')
				f.write(feature)
				f.write('#[allow(non_camel_case_types)]\n')
				f.write(f'type {functype_name} = extern "system" fn(')
				params = []
				for param_name, param_type in func_data['params'].items():
					param_name, param_type = process_guts(param_name, param_type, is_param = True)
					params += [f'{param_name}: {param_type}']
				f.write(', '.join(params))
				ret_type = func_data["ret_type"]
				if ret_type == 'void':
					f.write(f');\n')
				else:
					f.write(f') -> {ctype_to_rust(ret_type)};\n')
		process_constants(f)
		process_typedefs(f)
		process_handles(f)
		process_enums(f)
		process_unions(f)
		process_structs(f)
		proc_protos(f)
		dummys = io.StringIO()
		traits = io.StringIO()
		struct = io.StringIO()
		t_impl = io.StringIO()
		d_impl = io.StringIO()
		s_impl = io.StringIO()
		g_impl = io.StringIO()
		struct_version = to_camel(f'VULKAN_{version.split("_", 1)[-1]}'.lower(), True)
		snake_version = to_snake(version)
		traits.write(f'/// trait for `{version}`\n')
		if not version.startswith('StdVideo'):
			traits.write(f'/// - Reference: <https://registry.khronos.org/vulkan/specs/latest/man/html/{version}.html>\n')
		traits.write(feature)
		traits.write('#[allow(non_camel_case_types)]\n')
		traits.write(f'pub trait {version}: Debug {{')
		struct.write(f'/// struct for `{version}`\n')
		struct.write(feature)
		if len(funcs):
			struct.write(f'#[derive(Clone, Copy)]\n')
		else:
			struct.write(f'#[derive(Default, Debug, Clone, Copy)]\n')
		struct.write(f'pub struct {struct_version} {{')
		t_impl.write(feature)
		t_impl.write(f'impl {version} for {struct_version} {{')
		if len(funcs):
			d_impl.write(feature)
			d_impl.write(f'impl Default for {struct_version} {{\n')
			d_impl.write('\tfn default() -> Self {\n')
		s_impl.write(feature)
		s_impl.write(f'impl {struct_version} {{\n')
		if len(funcs):
			g_impl.write(feature)
			g_impl.write(f'impl Debug for {struct_version} {{\n')
			g_impl.write('\tfn fmt(&self, f: &mut Formatter) -> fmt::Result {\n')
			g_impl.write(f'\t\tf.debug_struct("{struct_version}")\n')
		vk_struct.write(f'\t/// Subset of {version}\n')
		vk_struct.write(feature_indent)
		vk_struct.write(f'\t{snake_version}: {struct_version},\n')
		vk_g_impl.write(f'\t/// Get the subset of {version}\n')
		vk_g_impl.write(feature_indent)
		vk_g_impl.write(f'\tpub fn get_{snake_version}(&self) -> &{struct_version} {{\n')
		vk_g_impl.write(f'\t\t&self.{snake_version}\n')
		vk_g_impl.write('\t}\n')
		vk_traits.write(feature)
		vk_traits.write(f'impl {version} for VkCore {{')
		vk_s_impl.write(feature_indent_3)
		vk_s_impl.write(f'\t\t\t{snake_version}: {struct_version}::new(instance, &mut get_instance_proc_address),\n')
		snakes = {}
		if len(funcs):
			traits.write('\n')
			vk_traits.write('\n')
			s_impl.write("\tpub fn new(instance: VkInstance, mut get_instance_proc_address: impl FnMut(VkInstance, &'static str) -> *const c_void) -> Self {\n")
			s_impl.write('\t\tSelf {\n')
			d_impl.write('\t\tSelf {\n')
			t_impl.write('\n')
			struct.write('\n')
		else:
			s_impl.write("\tpub fn new(_instance: VkInstance, _get_instance_proc_address: impl FnMut(VkInstance, &'static str) -> *const c_void) -> Self {\n")
			s_impl.write('\t\tSelf {')
		for func in funcs:
			func_snake = to_snake(func)
			snakes[func_snake] = func
			func_data = func_protos[f'PFN_{func}']
			params = []
			params_dummy = []
			param_call = []
			param_proto = []
			ret_type = func_data["ret_type"]
			ret_type_rust = ctype_to_rust(ret_type)
			for param_name, param_type in func_data['params'].items():
				param_name, param_type = process_guts(param_name, param_type, is_param = True)
				params += [f'{param_name}: {param_type}']
				params_dummy += [f'_: {param_type}']
				param_call += [param_name]
				param_proto += [param_type]
			dummys.write(f'/// The dummy function for `{func}` from `{version}`\n')
			dummys.write(feature)
			dummys.write(f'extern "system" fn dummy_{func}({", ".join(params_dummy)})')
			traits.write(f'\t/// - Reference: <https://registry.khronos.org/vulkan/specs/latest/man/html/{func}.html>\n')
			traits.write(f'\tfn {func}(&self, {", ".join(params)})')
			t_impl.write('\t#[inline(always)]\n')
			t_impl.write(f'\tfn {func}(&self, {", ".join(params)})')
			vk_traits.write('\t#[inline(always)]\n')
			vk_traits.write(f'\tfn {func}(&self, {", ".join(params)})')
			d_impl.write(f'\t\t\t{func_snake}: dummy_{func},\n')
			if ret_type == 'void':
				r_rettype_suffix = ''
				dummys.write(' {\n')
				traits.write(' -> Result<()>;\n')
				t_impl.write(' -> Result<()> {\n')
				vk_traits.write(' -> Result<()> {\n')
			elif ret_type == 'VkResult':
				r_rettype_suffix = f' -> {ret_type_rust}'
				dummys.write(f'{r_rettype_suffix} {{\n')
				traits.write(f' -> Result<()>;\n')
				t_impl.write(f' -> Result<()> {{\n')
				vk_traits.write(f' -> Result<()> {{\n')
			else:
				r_rettype_suffix = f' -> {ret_type_rust}'
				dummys.write(f'{r_rettype_suffix} {{\n')
				traits.write(f' -> Result<{ret_type_rust}>;\n')
				t_impl.write(f' -> Result<{ret_type_rust}> {{\n')
				vk_traits.write(f' -> Result<{ret_type_rust}> {{\n')
			dummys.write(f'\tpanic_any(VkError::NullFunctionPointer("{func}"))\n')
			dummys.write('}\n')
			s_impl.write(f'\t\t\t{func_snake}: {{let proc = get_instance_proc_address(instance, "{func}"); if proc.is_null() {{dummy_{func}}} else {{unsafe {{transmute(proc)}}}}}},\n')
			g_impl.write(f'\t\t.field("{func}", &if self.{func_snake} == dummy_{func} {{null::<c_void>()}} else {{self.{func_snake} as *const c_void}})\n')
			if ret_type == 'void':
				t_impl.write('\t\t#[cfg(feature = "catch_nullptr")]\n')
				t_impl.write(f'\t\treturn vk_process_catch(catch_unwind(||((self.{func_snake})({", ".join(param_call)}))));\n')
				t_impl.write('\t\t#[cfg(not(feature = "catch_nullptr"))]\n')
				t_impl.write(f'\t\treturn {{(self.{func_snake})({", ".join(param_call)}); Ok(())}};\n')
				vk_traits.write('\t\t#[cfg(feature = "catch_nullptr")]\n')
				vk_traits.write(f'\t\treturn vk_process_catch(catch_unwind(||((self.{snake_version}.{func_snake})({", ".join(param_call)}))));\n')
				vk_traits.write('\t\t#[cfg(not(feature = "catch_nullptr"))]\n')
				vk_traits.write(f'\t\treturn {{(self.{snake_version}.{func_snake})({", ".join(param_call)}); Ok(())}};\n')
			elif ret_type == 'VkResult':
				t_impl.write('\t\t#[cfg(feature = "catch_nullptr")]\n')
				t_impl.write(f'\t\treturn vk_convert_result("{func}", catch_unwind(||((self.{func_snake})({", ".join(param_call)}))));\n')
				t_impl.write('\t\t#[cfg(not(feature = "catch_nullptr"))]\n')
				t_impl.write(f'\t\treturn vk_result_conv("{func}", (self.{func_snake})({", ".join(param_call)}));\n')
				vk_traits.write('\t\t#[cfg(feature = "catch_nullptr")]\n')
				vk_traits.write(f'\t\treturn vk_convert_result("{func}", catch_unwind(||((self.{snake_version}.{func_snake})({", ".join(param_call)}))));\n')
				vk_traits.write('\t\t#[cfg(not(feature = "catch_nullptr"))]\n')
				vk_traits.write(f'\t\treturn vk_result_conv("{func}", (self.{snake_version}.{func_snake})({", ".join(param_call)}));\n')
			else:
				t_impl.write('\t\t#[cfg(feature = "catch_nullptr")]\n')
				t_impl.write(f'\t\treturn vk_process_catch(catch_unwind(||((self.{func_snake})({", ".join(param_call)}))));\n')
				t_impl.write('\t\t#[cfg(not(feature = "catch_nullptr"))]\n')
				t_impl.write(f'\t\treturn Ok((self.{func_snake})({", ".join(param_call)}));\n')
				vk_traits.write('\t\t#[cfg(feature = "catch_nullptr")]\n')
				vk_traits.write(f'\t\treturn vk_process_catch(catch_unwind(||((self.{snake_version}.{func_snake})({", ".join(param_call)}))));\n')
				vk_traits.write('\t\t#[cfg(not(feature = "catch_nullptr"))]\n')
				vk_traits.write(f'\t\treturn Ok((self.{snake_version}.{func_snake})({", ".join(param_call)}));\n')
			t_impl.write('\t}\n')
			vk_traits.write('\t}\n')
			struct.write(f'\t{func_snake}: PFN_{func},\n')
		traits.write('}\n')
		struct.write('}\n')
		t_impl.write('}\n')
		if len(funcs):
			d_impl.write('\t\t}\n')
			d_impl.write('\t}\n')
			d_impl.write('}\n')
			s_impl.write('\t\t}\n')
		else:
			s_impl.write('}\n')
		s_impl.write('\t}\n')
		s_impl.write('}\n')
		if len(funcs):
			g_impl.write('\t\t.finish()\n')
			g_impl.write('\t}\n')
			g_impl.write('}\n')
		vk_traits.write('}\n')
		f.write(dummys.getvalue())
		f.write(traits.getvalue())
		f.write(struct.getvalue())
		f.write(t_impl.getvalue())
		f.write(d_impl.getvalue())
		f.write(s_impl.getvalue())
		f.write(g_impl.getvalue())
	vkresult_enum = parsed['VK_VERSION_1_0']['enums']['VkResult']
	with open(outfile, 'w') as f:
		f.write('\n')
		f.write('#![allow(dead_code)]\n')
		f.write('#![allow(non_snake_case)]\n')
		f.write('#![allow(non_upper_case_globals)]\n')
		f.write('#![allow(unpredictable_function_pointer_comparisons)]\n')
		f.write('#![allow(clippy::too_many_arguments)]\n')
		f.write('#![allow(clippy::missing_transmute_annotations)]\n')
		f.write('\n')
		f.write('use std::{\n')
		f.write('\tcollections::BTreeSet,\n')
		f.write('\tffi::{c_void, CStr, CString},\n')
		f.write('\tfmt::{self, Debug, Formatter},\n')
		f.write('\tpanic::panic_any,\n')
		f.write('\tmem::transmute,\n')
		f.write('\tops::{BitAnd, BitOr, BitXor, Deref, Not},\n')
		f.write('\tptr::{null, null_mut},\n')
		f.write('\tsync::Arc,\n')
		f.write('};\n')
		f.write('#[cfg(feature = "catch_nullptr")]\n')
		f.write('use std::panic::{catch_unwind, resume_unwind};\n')
		f.write('\n')
		f.write('/// Make a version value\n')
		f.write('pub fn vk_make_version(major: u32, minor: u32, patch: u32) -> u32 {\n')
		f.write('\t(major << 22) | (minor << 12) | patch\n')
		f.write('}\n')
		f.write('/// Make an API version value\n')
		f.write('pub fn vk_make_api_version(variant: u32, major: u32, minor: u32, patch: u32) -> u32 {\n')
		f.write('\t(variant << 29) | (major << 22) | (minor << 12) | patch\n')
		f.write('}\n')
		f.write('/// Make a video standard version value\n')
		f.write('pub fn vk_make_video_std_version(major: u32, minor: u32, patch: u32) -> u32 {\n')
		f.write('\t(major << 22) | (minor << 12) | patch\n')
		f.write('}\n')
		f.write('\n')
		f.write('/// Convert byte array to a string that represents the data of the array.\n')
		f.write('pub fn vk_to_byte_array_string<const N: usize>(input: &[u8; N]) -> String {\n')
		f.write('\tformat!("[{}]", input.iter().map(|b|format!("0x{b:02X}")).collect::<Vec<String>>().join(", "))\n')
		f.write('}\n')
		f.write('\n')
		f.write('/// Convert a fixed-length `i8` array to a Rust string if it is a UTF-8 string; otherwise, return the hexadecimal sequences of the byte array\n')
		f.write('pub fn vk_format_maybe_string<const N: usize>(input: &[i8; N]) -> String {\n')
		f.write('\tmatch unsafe{CStr::from_ptr(input.as_ptr())}.to_str() {\n')
		f.write('\t\tOk(s) => format!("\\"{s}\\""),\n')
		f.write('\t\tErr(_) => vk_to_byte_array_string::<N>(unsafe{transmute(input)}),\n')
		f.write('\t}\n')
		f.write('}\n')
		f.write('\n')
		f.write('/// The `Result` type for the Vulkan APIs\n')
		f.write('#[derive(Debug, Clone)]\n')
		f.write('pub enum VkError {\n')
		f.write('\tNullFunctionPointer(&\'static str),\n')
		for vkresult, result_value in vkresult_enum.items():
			if vkresult == 'VK_SUCCESS': continue
			if result_value in vkresult_enum: continue
			f.write(f'\t{to_camel(vkresult, True)}(&\'static str),\n')
		f.write('\tUnknownError((VkResult, &\'static str)),\n')
		f.write('}\n')
		f.write('\n')
		f.write('/// Our result type for all of the Vulkan function wrappers\n')
		f.write('type Result<T> = std::result::Result<T, VkError>;\n')
		f.write('\n')
		f.write('/// Translate the returned `Result<T>` from `std::panic::catch_unwind()` to our `Result<T>`\n')
		f.write('#[cfg(feature = "catch_nullptr")]\n')
		f.write('#[inline(always)]\n')
		f.write('pub fn vk_process_catch<T>(ret: std::thread::Result<T>) -> Result<T> {\n')
		f.write('\tmatch ret {\n')
		f.write('\t\tOk(ret) => Ok(ret),\n')
		f.write('\t\tErr(e) => {\n')
		f.write('\t\t\tif let Some(e) = e.downcast_ref::<VkError>() {\n')
		f.write('\t\t\t\tErr(e.clone())\n')
		f.write('\t\t\t} else {\n')
		f.write('\t\t\t\tresume_unwind(e)\n')
		f.write('\t\t\t}\n')
		f.write('\t\t}\n')
		f.write('\t}\n')
		f.write('}\n')
		f.write('\n')
		f.write('/// Convert a `VkResult` to our `Result<()>` \n')
		f.write('#[inline(always)]\n')
		f.write('pub fn vk_result_conv(function_name: &\'static str, result: VkResult) -> Result<()> {\n')
		f.write('\tmatch result {\n')
		f.write('\t\tVkResult::VK_SUCCESS => Ok(()),\n')
		for vkresult, result_value in vkresult_enum.items():
			if vkresult == 'VK_SUCCESS': continue
			if result_value in vkresult_enum: continue
			f.write(f'\t\tVkResult::{vkresult} => Err(VkError::{to_camel(vkresult, True)}(function_name)),\n')
		f.write('\t}\n')
		f.write('}\n')
		f.write('\n')
		f.write('/// Convert a result returned from `std::panic::catch_unwind()` with `VkResult` to our `Result<()>` \n')
		f.write('#[cfg(feature = "catch_nullptr")]\n')
		f.write('#[inline(always)]\n')
		f.write('pub fn vk_convert_result(function_name: &\'static str, result: std::thread::Result<VkResult>) -> Result<()> {\n')
		f.write('\tif let Ok(result) = result {\n')
		f.write('\t\tvk_result_conv(function_name, result)\n')
		f.write('\t} else {\n')
		f.write('\t\tErr(VkError::NullFunctionPointer(function_name))\n')
		f.write('\t}\n')
		f.write('}\n')
		f.write('\n')
		f.write('impl From<VkError> for VkResult {\n')
		f.write('\tfn from(val: VkError) -> Self {\n')
		f.write('\t\tmatch val {\n')
		for vkresult, result_value in vkresult_enum.items():
			if vkresult == 'VK_SUCCESS': continue
			if result_value in vkresult_enum: continue
			f.write(f'\t\t\tVkError::{to_camel(vkresult, True)}(_) => VkResult::{vkresult},\n')
		f.write('\t\t\t_ => panic!("No `VkResult` value to `{val:?}`"),\n')
		f.write('\t\t}\n')
		f.write('\t}\n')
		f.write('}\n')
		f.write('\n')
		for version, verdata in parsed.items():
			if version == 'metadata':
				continue
			process_version(version, verdata, f)
		vk_struct.write('}\n')
		vk_s_impl.write('\t\t})\n')
		vk_s_impl.write('\t}\n')
		vk_s_impl.write('\n')
		vk_s_impl.write(vk_g_impl.getvalue())
		vk_g_impl = None
		vk_s_impl.write('\n')
		vk_s_impl.write('\t/// Get `VkInstance`\n')
		vk_s_impl.write('\tpub fn get_instance(&self) -> VkInstance {\n')
		vk_s_impl.write('\t\t**self.instance\n')
		vk_s_impl.write('\t}\n')
		vk_s_impl.write('\t/// Get the application info\n')
		vk_s_impl.write('\tpub fn get_app_info(&self) -> &VkApplicationInfo {\n')
		vk_s_impl.write('\t\t&self.app_info\n')
		vk_s_impl.write('\t}\n')
		vk_s_impl.write('\t/// Get extensions\n')
		vk_s_impl.write('\tpub fn get_extensions(&self) -> &BTreeSet<String> {\n')
		vk_s_impl.write('\t\t&self.extensions\n')
		vk_s_impl.write('\t}\n')
		vk_s_impl.write('}\n')
		vk_s_impl.write('\n')
		vk_s_impl.write('impl Drop for VkCore {\n')
		vk_s_impl.write('\tfn drop(&mut self) {\n')
		vk_s_impl.write('\t\tself.vkDestroyInstance(self.get_instance(), null()).unwrap();\n')
		vk_s_impl.write('\t}\n')
		vk_s_impl.write('}\n')
		vk_s_impl.write('\n')
		vk_s_impl.write('unsafe impl Send for VkCore {}\n')
		vk_s_impl.write('unsafe impl Sync for VkCore {}\n')
		vk_s_impl.write('\n')
		f.write(vk_struct.getvalue())
		f.write(vk_traits.getvalue())
		f.write(vk_s_impl.getvalue())
		f.write('\n')
		f.write('#[cfg(any(feature = "glfw", test))]\n')
		f.write('mod glfw_create_surface {\n')
		f.write('\tuse crate::*;\n')
		f.write('\tuse glfw::*;\n')
		f.write('\tuse glfw::ffi::*;\n')
		f.write('\tunsafe extern "C" {\n')
		f.write('\t\tfn glfwCreateWindowSurface(instance: VkInstance, window: *const GLFWwindow, allocator: *const VkAllocationCallbacks, surface: *mut VkSurfaceKHR) -> VkResult;\n')
		f.write('\t}\n')
		f.write('\t/// The function for you to create a `VkSurfaceKHR` when the feature "glfw" is enabled\n')
		f.write('\tpub fn vkCreateWindowSurfaceGLFW(instance: VkInstance, window: &PWindow, allocator: *const VkAllocationCallbacks, surface: *mut VkSurfaceKHR) -> VkResult {\n')
		f.write('\t\tunsafe {glfwCreateWindowSurface(instance, window.window_ptr(), allocator, surface)}\n')
		f.write('\t}\n')
		f.write('}\n')
		f.write('\n')
		f.write('#[cfg(any(feature = "glfw", test))]\n')
		f.write('pub use glfw_create_surface::vkCreateWindowSurfaceGLFW;\n')


if __name__ == '__main__':
	basic_typedefs = {
		'int8_t': 'i8',
		'int16_t': 'i16',
		'int32_t': 'i32',
		'int64_t': 'i64',
		'uint8_t': 'u8',
		'uint16_t': 'u16',
		'uint32_t': 'u32',
		'uint64_t': 'u64',
		'size_t': 'usize',
		'char': 'i8',
		'short': 'i16',
		'int': 'i32',
		'unsigned': 'u32',
		'long': 'i64',
		'float': 'f32',
		'double': 'f64',
	}
	basic_aliases = basic_typedefs
	wayland_typedefs = {
		'struct wl_display*': '*const c_void',
		'struct wl_surface*': '*const c_void',
	}
	wayland_aliases = {
		'struct wl_display*': 'wl_display',
		'struct wl_surface*': 'wl_surface',
	}
	win32_typedefs = {
		'LPCWSTR': '*const i16',
		'DWORD': 'u32',
		'BOOL': 'u32',
	}
	win32_handles = [
		'HINSTANCE',
		'HANDLE',
		'HWND',
		'HMONITOR',
	]
	win32_structs = {
		'SECURITY_ATTRIBUTES': {
			'nLength': 'DWORD',
			'lpSecurityDescriptor': 'const void*',
			'bInheritHandle': 'BOOL',
		}
	}
	xcb_typedefs = {
		'xcb_connection_t*': '*const c_void',
		'xcb_window_t': 'uint32_t',
		'xcb_visualid_t': 'uint32_t',
	}
	parsed = parse('vulkan_core.h', typedefs = basic_typedefs, aliases = basic_aliases)
	parsed = parse('vulkan_android.h', parsed, 1, feature_name = "android_khr")
	parsed = parse('vulkan_ios.h', parsed, 1, feature_name = "ios_mvk")
	parsed = parse('vulkan_macos.h', parsed, 1, feature_name = "macos_mvk")
	parsed = parse('vulkan_metal.h', parsed, 1, feature_name = "metal_ext")
	parsed = parse('vulkan_wayland.h', parsed, 1, typedefs = wayland_typedefs, aliases = wayland_aliases, feature_name = "wayland_khr")
	parsed = parse('vulkan_win32.h', parsed, 1, handles = win32_handles, typedefs = win32_typedefs, structs = win32_structs, feature_name = "win32_khr")
	parsed = parse('vulkan_xcb.h', parsed, 1, typedefs = xcb_typedefs, feature_name = "xcb_khr")
	with open('vkcore.json', 'w') as f:
		json.dump(parsed, f, indent=4)
	to_rust('vkcore.rs', parsed)
